home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Developer / Eval / Source / Eval.m < prev    next >
Text File  |  1992-09-17  |  17KB  |  575 lines

  1. #import "Eval.h"
  2. #import "EvalWrap.h"
  3. #import "GraphicsView.h"
  4. #import "CString.h"
  5. #import <appkit/Text.h>
  6. #import <appkit/ScrollView.h>
  7. #import <appkit/Listener.h>
  8. #import <appkit/Pasteboard.h>
  9. #import <appkit/NXSplitView.h>
  10. #import <appkit/NXBrowser.h>
  11. #import <appkit/NXBrowserCell.h>
  12. #import <appkit/Matrix.h>
  13. #import <appkit/OpenPanel.h>
  14. #import <appkit/Font.h>
  15. #import <appkit/Pasteboard.h>
  16. #import <defaults/defaults.h>
  17. #import <objc/List.h>
  18. #import <objc/objc-load.h>
  19. #import <objc/error.h>
  20. #import <streams/streams.h>
  21. #import <sys/stat.h>
  22. #import <sys/stat.h>
  23. #import <libc.h>
  24. #import <ctype.h>
  25. #import <dpsclient/psops.h>
  26. #import <dpsclient/dpsclient.h>
  27. #import <streams/streams.h>
  28. id text, graphics ;
  29.  
  30. static NXDefaultsVector evalDefaults = 
  31. { {"NXFont","Ohlfs"},
  32.   {"NXFontSize","11.0"},
  33.   {"Frame","100.0 100.0 300.0 300.0"},
  34.   {"GraphicsSize","400.0 400.0"},
  35.   {"CompilerSwitches",""},
  36.   {"Libraries","/lib/libsys_s.a /usr/lib/libNeXT_s.a"},
  37.   {NULL} 
  38. } ;
  39.  
  40. // archive (library) extension 
  41. const char *libTypes[] = 
  42. { "a",
  43.   NULL 
  44. } ;
  45.  
  46. @implementation Eval: Application
  47. { id transcriptWindow ;
  48.   id  splitView ;
  49.   id  transcriptText ;
  50.   id  transcriptGraphics ;
  51.   id  textScrollView ;
  52.   id  graphicsScrollView ;
  53.   // pref thingies
  54.   id  fontName ;
  55.   id  fontSize ;
  56.   id  graphicsSize ;
  57.   id  compilerSwitches ;
  58.   id  libBrowser ;
  59.   id  libList ;
  60. }
  61.  
  62. + clearGraphics ;
  63. { return [graphics clear] ;
  64. }
  65.  
  66. + clearText ;
  67. { return [text setText: ""] ;
  68. }
  69.  
  70. + printf: (char *) format, ... ;
  71. { // format (as in printf) and print to the transcript
  72.   NXStream *aStream ;
  73.   char *textBuf ;
  74.   int textLen, maxLen ;
  75.   va_list argList ;
  76.   aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
  77.   va_start(argList, format) ;
  78.   NXVPrintf(aStream, format, argList) ;
  79.   NXGetMemoryBuffer(aStream, &textBuf, &textLen, &maxLen);
  80.   textLen = [text textLength] ;
  81.   [text setSel: textLen :textLen] ;
  82.   [text replaceSel: textBuf] ;
  83.   NXCloseMemory(aStream,NX_TRUNCATEBUFFER) ;
  84.   [[text window] orderFront: self] ;
  85.   return self ;
  86. }
  87.  
  88. void SwitchContextsWithFocus(DPSContext newContext)
  89. { // borowsed from YAP: switch to newContext in order to
  90.   // protect the main context
  91.   float c1x, c1y, c2x, c2y;
  92.   float winCTM[6];
  93.   int realWinNum;
  94.   GetFocus(&c1x, &c1y, &c2x, &c2y, winCTM, &realWinNum);
  95.   DPSSetContext(newContext);
  96.   ReFocus(realWinNum, winCTM, c1x, c1y, c2x, c2y);
  97. }
  98.  
  99. + ps: (char *) format, ... ;
  100. { // format (as in printf) and send to windowserver.
  101.   // Done in a separate context to provide good
  102.   // error handling and recovery.  The idea is stolen
  103.   // from Yap.
  104.   NXStream *dataStream, *errorStream ;
  105.   char *dataBuffer ;
  106.   va_list argList ;
  107.   int len, maxLen ;
  108.   NXHandler exception;
  109.   static DPSContext NUContext = NULL; // The second context
  110.   DPSContext curContext = DPSGetCurrentContext();
  111.  
  112.   [[graphics image] lockFocus] ;
  113.   if(!NUContext) // create the second context
  114.   { const char *app = [NXApp appName];
  115.     NUContext = DPSCreateContext(NXGetDefaultValue(app, "NXHost"),
  116.         NXGetDefaultValue(app, "NXPSName"), NULL, NULL);
  117.     DPSSetContext(curContext);
  118.   }
  119.   dataStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
  120.   va_start(argList, format) ;
  121.   NXVPrintf(dataStream, format, argList) ;
  122.   NXPutc(dataStream,'\n') ;
  123.   NXGetMemoryBuffer(dataStream, &dataBuffer, &len, &maxLen);
  124.   SwitchContextsWithFocus(NUContext);
  125.   exception.code = 0 ;
  126.   NX_DURING
  127.     DPSWriteData(NUContext,dataBuffer,len) ;
  128.     NXPing (); 
  129.   NX_HANDLER
  130.     exception = NXLocalHandler ;
  131.   NX_ENDHANDLER
  132.   DPSSetContext(curContext);
  133.   NXCloseMemory(dataStream,NX_FREEBUFFER) ;
  134.   if(exception.code)
  135.   { NXRunAlertPanel("Eval","Postcript errors in message\n"
  136.        "to [ps...] : see transcript",NULL,NULL,NULL) ;
  137.     errorStream = NXOpenMemory(NULL,0,NX_WRITEONLY);
  138.     DPSPrintErrorToStream(errorStream,
  139.        (DPSBinObjSeq) exception.data2);
  140.     NXPrintf(errorStream,"\n\0") ;
  141.     NXFlush(errorStream);
  142.     NXGetMemoryBuffer(errorStream, &dataBuffer, &len, &maxLen);
  143.     [Eval printf: "%s\n", dataBuffer] ;
  144.     NXCloseMemory(errorStream, NX_FREEBUFFER);
  145.     DPSDestroyContext(NUContext);
  146.     NUContext = NULL;
  147.   }
  148.   [[graphics image] unlockFocus] ;
  149.   [[graphics window] display] ;
  150.   [[graphics window] orderFront: self] ;
  151.   return self;
  152. }
  153.  
  154.  
  155. + startPS ;
  156. { // lock focus on graphics image
  157.   [[graphics image] lockFocus] ;
  158.   return self ;
  159. }
  160.  
  161. + stopPS ;
  162. { // unlock focus on graphics image ; display
  163.   // the graphics
  164.   [[graphics image] unlockFocus] ;
  165.   [graphics display] ;
  166.   [[graphics window] orderFront: self] ;
  167.   return self ;
  168. }
  169.  
  170. - appDidInit: sender ;
  171. {  // set myself as the services delegate
  172.    [[self appListener] setServicesDelegate: self] ;
  173.    return self ;
  174. }
  175.  
  176. - appendFileToTranscript: (char *) fileName ;
  177. { // append the text of the indicated file to the transcript
  178.   char *aStr ;
  179.   struct stat statBuf ;
  180.   int fd ;
  181.   if(!(fd = open(fileName,O_RDONLY)))
  182.   { NXRunAlertPanel("Eval","Cannot open: %c",NULL,NULL,NULL,fileName) ;
  183.     return self ;
  184.   }
  185.   fstat(fd,&statBuf) ;
  186.   aStr = malloc(statBuf.st_size + 1) ;
  187.   read(fd,aStr,statBuf.st_size) ;
  188.   aStr[statBuf.st_size] = '\0' ;
  189.   [Eval printf: aStr] ;
  190.   close(fd) ;
  191.   free(aStr) ;
  192.   return self ;
  193. }
  194.  
  195. - (int)browser:sender fillMatrix:matrix inColumn:(int)column ;
  196. { // delegate method for browser object 
  197.   return [libList count] ;
  198. }
  199.  
  200. - browser:sender loadCell:cell atRow:(int)row inColumn:(int)column ;
  201. {   [cell setStringValue: [[libList objectAt: row] cString]] ;
  202.     [cell setLeaf: YES] ;
  203.    return self ;
  204. }
  205.  
  206. - clearTranscriptGraphics: sender ;
  207. { return [Eval clearGraphics] ;
  208. }
  209.  
  210. - clearTranscriptText: sender ;
  211. { return [Eval clearText] ;
  212. }
  213.  
  214.  
  215. - evalObjC: (char *) text length: (int) len ;
  216. { // evaluate objc in text (not necessarily null-terminated)...
  217.   // of length len
  218.   char textBuf[len+1] , aStr[128] ;
  219.   char *header, *body, *cursor ;
  220.   int moduleKnt ;
  221.   char tmpFile[20] = "EvalXXXXXX" ;
  222.   NXStream *aStream ;
  223.   FILE *fp ;
  224.   int i, textLen, maxLen ;
  225.   // copy the message so we can sever it in two
  226.   strncpy(textBuf,text,len) ;
  227.   // don't know if text is null terminated
  228.   textBuf[len] = '\0' ;
  229.   // find out if we have any header
  230.   header = body = textBuf ;
  231.   // move header pointer to start of header
  232.   while(isspace(*header)) 
  233.      header++ ;  // scan past any initial whiteSpace
  234.   if(*header == '#') // then we have a header
  235.   { // move body pointer to start of body
  236.     body = header ;
  237.     while(strncmp(body++,"\n\n", 2)) // "empty" line is end of header  
  238.     { if(*body == '\0') // then header ends without a body
  239.        { NXRunAlertPanel("Eval", "No body to evaluate.\n",
  240.              NULL,NULL,NULL) ;
  241.          return self ;
  242.        }
  243.     }
  244.     *body++ = '\0' ; // make body point 1 char beyond end of header,
  245.                      // then sever textBuf into 2 strings (header and body) ;    
  246.   }
  247.   else
  248.     header = "" ;   // no header at all
  249.  
  250.   // The string tmpFile is both the name of the class we will create
  251.   // and the name of the file, within /tmp,  it will live in...
  252.   mktemp(tmpFile) ;
  253.   sprintf(aStr,"/tmp/%s.m",tmpFile) ;
  254.   fp = fopen(aStr,"w+") ;
  255.   fprintf(fp,
  256.         "%s\n"
  257.         "#import <objc/Object.h>\n"
  258.     "@interface Eval: Object\n"
  259.         "+ clearGraphics ;\n"
  260.         "+ clearText ;\n"
  261.     "+ printf: (char *) format, ... ;\n"
  262.     "+ ps: (char *) format, ... ;\n"
  263.         "+ startPS ;\n"
  264.         "+ stopPS ;\n"
  265.     "@end\n"
  266.        "@interface %s: Object\n"
  267.        "+(void) doit ;\n"
  268.        "@end\n\n"
  269.        "@implementation %s\n"
  270.        "+ (void) doit\n"
  271.        "{\n"
  272.        "#line 1\n"
  273.        "%s\n"
  274.        "}\n"
  275.        "@end",
  276.   header, tmpFile, tmpFile, body) ;
  277.   fflush(fp) ;
  278.   fclose(fp) ;
  279.   
  280.   // compile the class defined in this file
  281.   sprintf(aStr, "cc -c -o %s /tmp/%s.o /tmp/%s.m 2> /tmp/%s.cc_errors",
  282.       NXGetDefaultValue([NXApp appName],"CompilerSwitches"),
  283.       tmpFile, tmpFile, tmpFile) ;      
  284.   if(system(aStr)) // non-zero exit status == must report the errors
  285.   { sprintf(aStr,"/tmp/%s.cc_errors",tmpFile) ;
  286.     [self appendFileToTranscript: aStr] ;
  287.     unlink(aStr) ;
  288.     NXRunAlertPanel("Eval","Compilation errors: see Transcript Window\n",
  289.            NULL,NULL,NULL) ;
  290.     sprintf(aStr,"/tmp/%s.m", tmpFile) ; unlink(aStr) ;
  291.     sprintf(aStr,"/tmp/%s.o",tmpFile) ; unlink(aStr) ;
  292.     return self ;
  293.   } 
  294.   // dynamically link the class
  295.   moduleKnt = [libList count] ;
  296.   { // block down to dynamically alloc moduleNames
  297.     char *moduleNames[moduleKnt + 2] ;
  298.     aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
  299.     sprintf(aStr,"/tmp/%s.o",tmpFile) ;
  300.     moduleNames[0] = aStr ;
  301.     for(i = 1 ; i <= moduleKnt ; i++)
  302.       moduleNames[i] = (char *)[[libList objectAt: i] cString] ;
  303.     moduleNames[i] = NULL ;
  304.     if(objc_loadModules(moduleNames, aStream, NULL, NULL, NULL))
  305.     { // load failed...
  306.       NXPutc(aStream,'\0') ; 
  307.       NXGetMemoryBuffer(aStream, &cursor, &textLen, &maxLen);
  308.       [Eval printf: cursor];
  309.       NXRunAlertPanel("Eval","Load failed, see transcript\nfor details",
  310.             NULL,NULL,NULL) ;
  311.     }
  312.     else 
  313.     { // load succeeded...execute the method, at last!
  314.       objc_msgSend(objc_getClass(tmpFile), sel_getUid("doit")) ;
  315.       // now remove the module
  316.       objc_unloadModules(NULL, NULL) ;
  317.     }
  318.   }
  319.   NXCloseMemory(aStream,NX_TRUNCATEBUFFER) ;
  320.   sprintf(aStr,"/tmp/%s.m",tmpFile) ; unlink(aStr) ;
  321.   sprintf(aStr,"/tmp/%s.o",tmpFile) ; unlink(aStr) ;
  322.   sprintf(aStr,"/tmp/%s.cc_errors",tmpFile)  ;  unlink(aStr) ;
  323.   return self ;
  324. }
  325.  
  326. - evalObjCTranscript: sender ;
  327. { // evaluate the current transcript selection as ObjC
  328.  // get the selected text
  329.   NXSelPt start, end ;
  330.   char *textBuf ;
  331.   int textLen ;
  332.   [transcriptText getSel:&start :&end] ;
  333.   textLen = end.cp-start.cp ;
  334.   textBuf = (char *) alloca(textLen+1) ; 
  335.   [transcriptText getSubstring:textBuf start:start.cp length:textLen] ;
  336.   [self evalObjC:textBuf length:textLen] ; 
  337.   return self ;
  338. }
  339.  
  340. - evalObjC:(id) pboard userData: (const char *) uBuf error: (char **) msg ;
  341. { // compile and run the selection
  342.   char *text ;
  343.   const char *const *types ;
  344.   int i, textLen ;
  345.   BOOL found = NO ;
  346.   // fetch the data from pasteboard
  347.   types = [pboard types] ;
  348.   for(i = 0 ; types[i]; i++)
  349.   { if(!strcmp(types[i], NXAsciiPboardType))
  350.     { found = YES ;
  351.       break ;
  352.     }
  353.   }
  354.   if(!found) // no ascii found...
  355.     return self ;
  356.   [pboard readType:NXAsciiPboardType data:&text length:&textLen];
  357.   if(!text || !textLen) // nothing there...
  358.     return self ;
  359.   else
  360.     return [self evalObjC: text length: textLen] ;
  361. }
  362.  
  363. - evalPs: (id) pboard userData: (const char *) uBuf error: (char **) msg ;
  364. { // evaluate postscript from pasteboard
  365.   // fetch the data from pasteboard
  366.   const char *const *types ;
  367.   char *text, *buf ;
  368.   int textLen, i ;
  369.   BOOL found = NO ;
  370.   types = [pboard types] ;
  371.   for(i = 0 ; types[i]; i++)
  372.   { if(!strcmp(types[i], NXAsciiPboardType))
  373.     { found = YES ;
  374.       break ;
  375.     }
  376.   }
  377.   if(!found) // no ascii found...
  378.     return self ;
  379.   [pboard readType:NXAsciiPboardType data:&text length:&textLen];
  380.   if(!text || !textLen)
  381.     return self ;
  382.   // change text into a cstring
  383.   buf = alloca(textLen + 1) ;
  384.   strncpy(buf,text,textLen) ;
  385.   buf[textLen] = '\0' ;
  386.   [Eval ps: buf] ;
  387.   return self ;
  388. }
  389.  
  390.  
  391. - libAdd: sender ;
  392. { // add a library file's name to the list
  393.   // of libraries to be searched
  394.   id openPanel ;
  395.   char **theList, buf[256] ;
  396.  
  397.   openPanel = [OpenPanel new] ;
  398.   if([openPanel runModalForTypes: libTypes])
  399.   { theList = (char **) [openPanel filenames] ;
  400.     { sprintf(buf,"%s/%s",[openPanel directory],
  401.         *theList++) ;
  402.       [libList addObject: [CString new:buf]] ;
  403.     }
  404.     [libBrowser loadColumnZero] ;
  405.     [self setDefaults: self] ;
  406.   }  
  407.   return self ;   
  408. }
  409.  
  410. - libList ;
  411. { return libList ;
  412. }
  413.  
  414. - libRemove: sender ;
  415. { // remove a library file's name to the list
  416.   // of libraries to be searched
  417.   int theRow ;
  418.   theRow = [[libBrowser matrixInColumn: 0] selectedRow] ;
  419.   if(theRow >= 0)
  420.   { [[libList removeObjectAt: theRow] free] ;
  421.     [libBrowser loadColumnZero] ;
  422.     [self setDefaults: self] ;
  423.   }
  424.   return self ;
  425. }
  426.  
  427. - setDefaults: sender ;
  428. { // write info from defaults panel to defaults
  429.   // database and to objects as needed
  430.   int i, knt ;
  431.   char frameString[256], libString[2048] ;
  432.   NXRect aFrame, bFrame ;
  433.  
  434.   NXDefaultsVector theDefaults =
  435.   { {"NXFont",NULL},
  436.     {"NXFontSize",NULL},
  437.     {"Frame",NULL},
  438.     {"GraphicsSize",NULL},
  439.     {"Libraries",NULL},
  440.     {"CompilerSwitches",NULL},
  441.     {NULL}
  442.   } ;
  443.   theDefaults[0].value = (char *) [fontName stringValue] ;
  444.   theDefaults[1].value = (char *) [fontSize stringValue] ;
  445.   // set the frame preference
  446.   [transcriptWindow getFrame: &aFrame] ; 
  447.   [[transcriptWindow contentView] getFrame: &bFrame] ;
  448.   // Note: window's getFrame method returns the size of the
  449.   // window, whereas its sizeTo:: method sets the size of
  450.   // its contentView.  Its really the contentView we want to save
  451.   aFrame.size = bFrame.size ;
  452.   sprintf(frameString,"%f %f %f %f\n",
  453.    aFrame.origin.x,aFrame.origin.y,
  454.    aFrame.size.width,aFrame.size.height) ;
  455.   theDefaults[2].value = frameString ;
  456.   // set the graphicsSize preference
  457.   theDefaults[3].value = (char *) [graphicsSize stringValue] ;
  458.   // set the Libraries preference
  459.   knt = [libList count] ;
  460.   libString[0] = '\0' ;
  461.   for(i = 0 ; i < knt ; i++)
  462.   { strcat(libString,[[libList objectAt: i] cString]) ;
  463.     strcat(libString," ") ;
  464.   }
  465.   theDefaults[4].value = libString ;
  466.   // set the compiler options
  467.   theDefaults[5].value = (char *) [compilerSwitches stringValue] ;
  468.   NXWriteDefaults([NXApp appName], theDefaults) ; 
  469.   return self ;
  470. }
  471.  
  472.  
  473. - setGraphicsScrollView: sender ;
  474. { // stash the docView in both our ivasr, transcriptGraphics,
  475.   // and the global var graphics.
  476.   transcriptGraphics = graphics = [sender docView] ;
  477.   graphicsScrollView = sender ;
  478.   return [self setUp] ;
  479. }
  480.  
  481. - setGraphicsSize: sender ;
  482. { graphicsSize = sender ;
  483.   return [self setUp] ;
  484. }
  485.  
  486. - setSplitView: anObject ;
  487. { // sets the splitview and attach its subpanels
  488.   splitView = anObject ;
  489.   return [self setUp] ;
  490. }
  491.  
  492.  
  493. - setTextScrollView: sender ;
  494. { // stash the docView in both our ivar, transcriptText,
  495.   // and the global var transcript.
  496.   transcriptText = text = [sender docView] ;
  497.   textScrollView = sender ;
  498.   return [self setUp] ;
  499. }
  500.  
  501.  
  502.  
  503. - setUp ;
  504. { // if all my sockets have been filled, 
  505.   // do some setup work...
  506.   if(splitView && textScrollView && graphicsScrollView && graphicsSize)
  507.   { char aString[512] ;
  508.     const char *theAppName ;
  509.     const char *libraries ;
  510.     NXRect aRect ;
  511.     int i = 0, rval ;
  512.  
  513.     // put views into splitview
  514.     [splitView addSubview:
  515.     [graphicsScrollView removeFromSuperview]] ;
  516.     [splitView addSubview:
  517.     [textScrollView removeFromSuperview]] ;
  518.     // set up the defaults
  519.     theAppName = [NXApp appName] ;
  520.     // setup the defaults
  521.     NXRegisterDefaults(theAppName, evalDefaults) ;
  522.     // copy info from defaults vector to UI objects
  523.     [fontName setStringValue: NXGetDefaultValue(theAppName,"NXFont")] ;
  524.     [fontSize setStringValue: NXGetDefaultValue(theAppName,"NXFontSize")] ;
  525.     [graphicsSize setStringValue: NXGetDefaultValue(theAppName,"GraphicsSize")] ;
  526.     // init the libList, display in browser 
  527.     libList = [[List alloc] init] ;
  528.     libraries = NXGetDefaultValue(theAppName,"Libraries") ;
  529.     rval = sscanf(libraries,"%s",aString) ;
  530.     while(rval == 1)
  531.     { [libList addObject: [CString new: aString]] ;
  532.       while(libraries[i] &&
  533.        !isspace(libraries[i++])) ; // find next library
  534.       rval = sscanf(&libraries[i],"%s",aString) ;
  535.     }
  536.     [libBrowser loadColumnZero] ;
  537.     // display the compiler options
  538.     [compilerSwitches setStringValue: 
  539.            NXGetDefaultValue(theAppName,"CompilerSwitches")] ;
  540.     // now "apply" the defaults
  541.     [transcriptText setFont:
  542.       [Font newFont:NXGetDefaultValue(theAppName,"NXFont")
  543.         size:atof(NXGetDefaultValue(theAppName,"NXFontSize"))]] ;
  544.     sscanf(NXGetDefaultValue(theAppName,"Frame"),
  545.       "%f %f %f %f", &aRect.origin.x, &aRect.origin.y,
  546.       &aRect.size.width, &aRect.size.height) ;
  547.     [transcriptWindow sizeWindow: aRect.size.width :aRect.size.height] ;
  548.     [transcriptWindow moveTo:aRect.origin.x :aRect.origin.y] ;
  549.     // setup the graphics window
  550.     [graphics setUp] ;
  551.   }
  552.   return self ;
  553. }
  554.  
  555. - showTranscript: sender ;
  556. { [transcriptWindow orderFront: self] ;
  557.   return self ;
  558. }
  559.  
  560. - showTranscript: (id) pboard userData: (const char *) uBuf error: (char **) msg ;
  561. { [transcriptWindow orderFront: self] ;
  562.   return self ;
  563. }
  564.  
  565. - terminate: sender ;
  566. { // make sure defaults are up-to-date
  567.   [self setDefaults: self] ;
  568.   return [super terminate: self] ;
  569. }
  570.  
  571. - windowDidResize: sender ;
  572. { return [self setDefaults: self] ;
  573. }
  574.  
  575. @end